package org.xlp.db.bridge.defination;

import net.sf.cglib.proxy.MethodProxy;
import org.xlp.bean.base.IBeanCreator;
import org.xlp.bean.base.IBeanWrapper;
import org.xlp.bean.exception.BeanBaseException;
import org.xlp.bean.proxy.AbstractCglibProxy;
import org.xlp.db.bridge.annotation.Mapper;
import org.xlp.db.bridge.helper.DaoBridgeHelper;
import org.xlp.db.bridge.strategy.XmlSQLConstructorInfo;
import org.xlp.db.bridge.strategy.XmlSQLConstructorStrategyContext;
import org.xlp.db.bridge.type.MapperEnum;
import org.xlp.db.sql.PagedSQL;
import org.xlp.db.xml.SQLXmlContextApplication;
import org.xlp.db.xml.SQLXmlContextHelper;
import org.xlp.db.xml.XmlSQL;
import org.xlp.db.xml.ele.SQLEle;

import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;

/**
 * Description: mapper注解Bean创建器
 * <br/>date: 2024/4/16 14:31
 *
 * @version 1.0
 * @author: xlp
 * @see org.xlp.db.bridge.annotation.Mapper
 * @see IBeanCreator
 */
public class MapperAnnotationBeanCreator extends AbstractCglibProxy implements IBeanCreator {
    /**
     * 构造器
     *
     * @param targetClass bean类型
     * @throws NullPointerException 假如参数为null则抛出该异常
     */
    public MapperAnnotationBeanCreator(Class<?> targetClass) {
        super(targetClass);
    }

    /**
     * 构造器
     *
     * @param targetClass bean类型
     * @param classLoader 类加载器
     * @throws NullPointerException 假如第一个参数为null则抛出该异常
     */
    public MapperAnnotationBeanCreator(Class<?> targetClass, ClassLoader classLoader) {
        super(targetClass, classLoader);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
            throws Throwable {
        // 标记目标方法是否要进行额外的操作
        boolean targetMethodIsExecuteExtraOption = methodNeedExtraOption(o, method, objects);

        //直接执行类非抽象公共方法，不进行代理实现功能逻辑
        if (!Modifier.isAbstract(method.getModifiers()) && !getBeanClass().isInterface()){
            return executeNormalMethod(o, method, objects, methodProxy, targetMethodIsExecuteExtraOption);
        }

        //获取代理函数<code>Mapper</code>注解
        Mapper mapper = method.getAnnotation(Mapper.class);
        //如果函数没有此注解，直接返回null
        if (mapper == null) return null;

        if (!targetMethodIsExecuteExtraOption) {
            return executeSqlMethod(method, objects, mapper);
        }

        Object returnValue;
        try {
            ((IBeanWrapper) o).beforeExecute(method, objects);
            returnValue = executeSqlMethod(method, objects, mapper);
            //执行目标方法后需要执行的额外逻辑
            ((IBeanWrapper) o).afterExecute(method, objects);
        } catch (Throwable throwable) {
            ((IBeanWrapper) o).throwExecute(method, objects, throwable);
            throw throwable;
        }
        return returnValue;
    }

    private Object executeSqlMethod(Method method, Object[] objects, Mapper mapper)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        //获取代理函数返回类型
        Class<?> returnType = method.getReturnType();
        //获取代理函返回值泛型信息
        Type genericReturnType = method.getGenericReturnType();
        //获取代理函数参数类型数组
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取代理函数名称
        String methodName = method.getName();

        //获取执行操作类型
        MapperEnum mapperEnum = DbCRUDTypeUtils.getDbCRUDType(mapper, methodName, returnType);
        //获取构造信息
        XmlSQLConstructorStrategyContext strategyContext = new XmlSQLConstructorStrategyContext();
        strategyContext.addDefaultStrategies();
        XmlSQLConstructorInfo constructorInfo = strategyContext
                .getXmlSQLConstructor(genericReturnType, parameterTypes, objects);
        if (constructorInfo == null){
            throw new NoFindXmlSQLConstructorException("未找到：returnType[" + returnType +
                    "]，parameterTypes=[" + Arrays.toString(parameterTypes) + "]对应的XmlSQL类对应的构造函数！");
        }

        //获取SQLEle对象
        SQLXmlContextApplication application = SQLXmlContextHelper.getApplication();
        if (application == null){
            throw new SQLXmlContextApplicationException("未获取到SQLXmlContextApplication对象");
        }
        SQLEle sqlEle = application.getSqlEle(mapper.parentId(), mapper.id());
        List<Object> parameters = constructorInfo.getParameters();
        parameters.add(0, sqlEle);
        Constructor<? extends XmlSQL> constructor = constructorInfo.getConstructor();
        //使用对应的构造函数反射实例XmlSQL该对象
        XmlSQL xmlSQL = constructor.newInstance(parameters.toArray());

        if (xmlSQL instanceof PagedSQL) ((PagedSQL) xmlSQL).setLimit(constructorInfo.getLimit());

        return executeSql(xmlSQL, genericReturnType, mapperEnum);
    }

    /**
     * 执行sql逻辑
     * @param xmlSQL
     * @param genericReturnType
     * @param mapperEnum
     * @return
     */
    private Object executeSql(XmlSQL xmlSQL, Type genericReturnType, MapperEnum mapperEnum) {
        Object returnValue;
        boolean returnTypeIsVoid = checkReturnTypeIsVoid(genericReturnType);
        switch (mapperEnum){
            case SAVE:
               returnValue = DaoBridgeHelper.getBaseDao().save(xmlSQL);
               return returnTypeIsVoid ? null : returnValue;
            case CHANGE:
                returnValue = DaoBridgeHelper.getBaseDao().update(xmlSQL);
                return returnTypeIsVoid ? null : returnValue;
            case QUERY:
                return QueryExecuteUtils.executeQuery(xmlSQL, genericReturnType);
            default:
                return null;
        }
    }

    /**
     * 判断返回值类型是否为void
     * @param returnType
     * @return
     */
    private boolean checkReturnTypeIsVoid(Type returnType){
        return returnType == null || returnType == Void.TYPE || returnType == Void.class;
    }

    /**
     * 获取目标bean类型
     *
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> Class<T> getBeanClass() {
        return (Class<T>) getTargetClass();
    }

    /**
     * 创建bean对象
     *
     * @return
     * @throws BeanBaseException 假如创建bean实例失败，则抛出该异常
     */
    @Override
    public <T> T createBean() throws BeanBaseException {
        return createProxy();
    }

    /**
     * 执行类的非抽象public方法
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @param targetMethodIsExecuteExtraOption 标记目标方法是否要进行额外的操作
     * @return
     * @throws Throwable
     */
    private Object executeNormalMethod(Object o, Method method, Object[] objects, MethodProxy methodProxy,
                                       boolean targetMethodIsExecuteExtraOption) throws Throwable {
        Object returnValue;
        try {
            if (targetMethodIsExecuteExtraOption){
                ((IBeanWrapper) o).beforeExecute(method, objects);
            }
            returnValue = methodProxy.invokeSuper(o, objects);
            //执行目标方法后需要执行的额外逻辑
            if (targetMethodIsExecuteExtraOption){
                ((IBeanWrapper) o).afterExecute(method, objects);
            }
        } catch (Throwable throwable) {
            if (targetMethodIsExecuteExtraOption){
                ((IBeanWrapper) o).throwExecute(method, objects, throwable);
            }
            throw throwable;
        }
        return returnValue;
    }
}
